package com.cloud.open.controller;

import com.cloud.common.bean.PageInfo;
import com.cloud.common.bean.ResultInfo;
import com.cloud.open.constant.Constants;
import com.cloud.open.constant.AppStatusEnum;
import com.cloud.open.constant.ErrorEnum;
import com.cloud.open.dto.*;
import com.cloud.open.entity.App;
import com.cloud.open.entity.AppAuthorization;
import com.cloud.open.entity.Resource;
import com.cloud.open.entity.ResourceType;
import com.cloud.open.service.AppAuthorizationService;
import com.cloud.open.service.AppService;
import com.cloud.open.service.ResourceService;
import com.cloud.open.service.ResourceTypeService;
import com.cloud.open.support.SessionHelper;
import com.cloud.open.util.AppUtils;
import com.cloud.open.util.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 应用接口
 */
@Slf4j
@RestController
public class AppController {

    @Autowired
    private AppService appService;

    @Autowired
    private AppAuthorizationService appAuthorizationService;

    @Autowired
    private ResourceTypeService resourceTypeService;

    @Autowired
    private ResourceService resourceService;

    @Autowired
    private SessionHelper sessionHelper;

    /**
     * 查询应用
     *
     * @param paramDTO
     * @return ResultInfo
     */
    @PostMapping("/open/app/query")
    public ResultInfo<PageInfo<App>> query(@Valid @RequestBody AppQueryParamDTO paramDTO) {
        PageInfo<App> pageInfo = appService.getPage(paramDTO);
        return ResultInfo.success().setData(pageInfo);
    }

    /**
     * 添加应用
     *
     * @param paramDTO
     * @return ResultInfo
     */
    @PostMapping("/open/app/insert")
    public ResultInfo<String> insert(@Valid @RequestBody AppInsertParamDTO paramDTO) {
        if (appService.checkNameExist(paramDTO.getAppName(), null)) {
            return ErrorEnum.NAME_EXIST.resultInfo();
        }

        ResultInfo<String> resultInfo = checkWhitelistIp(paramDTO.getWhitelistIp().trim());
        if (!resultInfo.successful()) {
            return resultInfo;
        }

        App app = new App();
        app.setCreateTime(new Date());
        app.setAppStatus(AppStatusEnum.ENABLE.getCode());
        BeanUtils.copyProperties(paramDTO, app);

        app.setAppId(AppUtils.buildAppId());
        app.setAppSecret(AppUtils.buildAppSecret());
        if (appService.save(app)) {
            return ResultInfo.success();
        }
        return ResultInfo.failure();
    }

    /**
     * 更新应用
     *
     * @param paramDTO
     * @return ResultInfo
     */
    @PostMapping("/open/app/update")
    public ResultInfo<String> update(@Valid @RequestBody AppUpdateParamDTO paramDTO) {
        if (appService.checkNameExist(paramDTO.getAppName(), paramDTO.getId())) {
            return ErrorEnum.NAME_EXIST.resultInfo();
        }

        ResultInfo<String> resultInfo = checkWhitelistIp(paramDTO.getWhitelistIp().trim());
        if (!resultInfo.successful()) {
            return resultInfo;
        }

        App app = appService.getById(paramDTO.getId());
        if (app == null) {
            return ResultInfo.failure().setMessage("id not exist");
        }
        BeanUtils.copyProperties(paramDTO, app);

        if (appService.updateById(app)) {
            sessionHelper.refreshWhitelistIps(app.getId(), IPUtils.buildWhitelistIp(app.getWhitelistIp()));
            return ResultInfo.success();
        }
        return ResultInfo.failure();
    }

    /**
     * 更新应用状态
     *
     * @param paramDTO
     * @return ResultInfo
     */
    @PostMapping("/open/app/updateStatus")
    public ResultInfo<String> updateStatus(@Valid @RequestBody AppUpdateStatusParamDTO paramDTO) {
        App app = appService.getById(paramDTO.getId());
        if (app == null) {
            return ResultInfo.failure().setMessage("id not exist");
        }
        if (AppStatusEnum.of(paramDTO.getAppStatus()) == null) {
            return ErrorEnum.INVALID_STATUS.resultInfo();
        }
        app.setAppStatus(paramDTO.getAppStatus());
        if (appService.updateById(app)) {
            if (paramDTO.getAppStatus().intValue() == AppStatusEnum.DISABLE.getCode()) {
                sessionHelper.remove(app.getId());
            }
            return ResultInfo.success();
        }
        return ResultInfo.failure();
    }

    /**
     * 重置应用密钥
     *
     * @param id
     * @return ResultInfo
     */
    @PostMapping("/open/app/resetSecret")
    public ResultInfo<String> resetSecret(Integer id) {
        if (id == null) {
            return ErrorEnum.ID_NOT_NULL.resultInfo();
        }
        App app = appService.getById(id);
        if (app == null) {
            return ResultInfo.failure().setMessage("id not exist");
        }
        app.setAppSecret(AppUtils.buildAppSecret());
        if (appService.updateById(app)) {
            return ResultInfo.success();
        }
        return ResultInfo.failure();
    }

    /**
     * 删除应用
     *
     * @param id
     * @return ResultInfo
     */
    @PostMapping("/open/app/delete")
    public ResultInfo<String> delete(Integer id) {
        if (id == null) {
            return ErrorEnum.ID_NOT_NULL.resultInfo();
        }
        appService.removeById(id);
        sessionHelper.remove(id);
        return ResultInfo.success();
    }

    /**
     * 保存应用授权
     *
     * @param paramDTO
     * @return ResultInfo
     */
    @PostMapping("/open/app/saveAuthorizations")
    public ResultInfo<String> saveAuthorizations(@Valid @RequestBody AppSaveAuthorizationsParamDTO paramDTO) {
        // 检查应用是否存在
        App app = appService.getById(paramDTO.getAppId());
        if (app == null) {
            return ErrorEnum.APP_NOT_EXIST.resultInfo();
        }
        // 检查授权的资源是否存在
        List<Resource> resourceList = resourceService.getAll();
        if (CollectionUtils.isEmpty(resourceList)) {
            return ErrorEnum.RESOURCE_NOT_EXIST.resultInfo();
        }
        List<Integer> allResourceIds = resourceList.stream().map(Resource::getId).collect(Collectors.toList());
        if (!allResourceIds.containsAll(paramDTO.getResourceIds())) {
            return ErrorEnum.RESOURCE_NOT_EXIST.resultInfo();
        }

        if (appAuthorizationService.saveAuthorizations(paramDTO.getAppId(), paramDTO.getResourceIds())) {
            Set<String> authResources = resourceService.listByIds(paramDTO.getResourceIds()).stream().map(Resource::getAccessUri).collect(Collectors.toSet());
            sessionHelper.refreshAuthResources(app.getId(), authResources);
            return ResultInfo.success();
        }
        return ResultInfo.failure();
    }

    /**
     * 获取应用授权
     *
     * @param appId
     * @return ResultInfo
     */
    @PostMapping("/open/app/getAuthorizations")
    public ResultInfo<List<AuthorizationTypeDTO>> getAuthorizations(Integer appId) {
        if (appId == null) {
            return ErrorEnum.ID_NOT_NULL.resultInfo();
        }

        // 获取所有分类
        List<ResourceType> resourceTypeList = resourceTypeService.getAll();
        if (CollectionUtils.isEmpty(resourceTypeList)) {
            return ErrorEnum.PLEASE_ADD_RESOURCE_TYPE.resultInfo();
        }

        // 获取所有资源
        List<Resource> resourceList = resourceService.getAll();
        if (CollectionUtils.isEmpty(resourceList)) {
            return ErrorEnum.PLEASE_ADD_RESOURCE.resultInfo();
        }

        // 获取应用的授权资源ID
        List<AppAuthorization> appAuthorizationList = appAuthorizationService.getByAppId(appId);
        final Set<Integer> authorizationResourceIds = CollectionUtils.isEmpty(appAuthorizationList)
                ? Collections.EMPTY_SET : appAuthorizationList.stream().map(AppAuthorization::getResourceId).collect(Collectors.toSet());

        // 转换分类和资源的存储结构
        Map<Integer, ResourceType> resourceTypeMap = resourceTypeList.stream().collect(Collectors.toMap(ResourceType::getId, Function.identity()));
        Map<Integer, List<Resource>> resourceListMap = new TreeMap(resourceList.stream().collect(Collectors.groupingBy(Resource::getTypeId)));

        // 生成输出结果
        List<AuthorizationTypeDTO> resultList = new ArrayList<>(resourceTypeMap.size());
        resourceListMap.forEach((typeId, value) -> {
            // 处理分类
            AuthorizationTypeDTO authorizationTypeDTO = new AuthorizationTypeDTO();
            authorizationTypeDTO.setTypeId(typeId);
            authorizationTypeDTO.setTypeName(resourceTypeMap.get(typeId).getName());
            authorizationTypeDTO.setAuthorized(false);

            // 处理资源
            List<AuthorizationResourceDTO> authorizationResourceDTOList = new ArrayList<>(value.size());
            value.forEach(resource -> {
                AuthorizationResourceDTO authorizationResourceDTO = new AuthorizationResourceDTO();
                authorizationResourceDTO.setResourceId(resource.getId());
                authorizationResourceDTO.setResourceName(resource.getName());
                authorizationResourceDTO.setAuthorized(false);

                if (authorizationResourceIds.contains(resource.getId())) {
                    authorizationResourceDTO.setAuthorized(true);
                    authorizationTypeDTO.setAuthorized(true);
                }
                authorizationResourceDTOList.add(authorizationResourceDTO);
            });

            authorizationTypeDTO.setResourceList(authorizationResourceDTOList);
            resultList.add(authorizationTypeDTO);
        });

        return ResultInfo.success().setData(resultList);
    }

    private ResultInfo<String> checkWhitelistIp(String whitelistIp) {
        if (!whitelistIp.equals(Constants.ALL)) {
            Set<String> ips = IPUtils.buildWhitelistIp(whitelistIp);
            for (String ip : ips) {
                if (!IPUtils.isValid(ip.trim())) {
                    return ErrorEnum.IP_INCORRECT.resultInfo();
                }
            }
        }
        return ResultInfo.success();
    }
}

